﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RaptorEngine;
using Microsoft.Xna.Framework;
using OilRigSumoWrestlers.Controllables.Controllers;
using OilRigSumoWrestlers.Levels;

namespace OilRigSumoWrestlers.Artificial_intelligence
{
    /// <summary>
    /// A behavior for the AI focused on evading
    /// </summary>
    class EvadeBehavior : Behavior
    {
        private Vector3 _destination;
        private int _changeDestinationCount = 0;
        private GameTime _gameTime;
        //Avoid variables used to make the AI commit to avoiding obstacles for a
        //certain time to prevent hysteria
        private int _avoidCount = 0;
        private Vector2 _avoidDirection;

        public EvadeBehavior(Entity agent, Entity enemy, PlayerControllable controllable, ALevel level)
            : base(agent, enemy, controllable, level)
        {
            _destination = agent.Position;
        }

        public override void Update(GameTime gameTime)
        {
            _gameTime = gameTime;

            //avoid hysteric behavior
            if (agentControllable.PushCooldown > 0 && agentControllable.PushCooldown < 300)
            {
                //Nothing need to be done here, I just couldn't write an inverted if-case
            }
            else
            {
                if (DistanceToEnemy < GameConstant.AI_MAX_PUSH_DIST && ApproachingEnemy)
                {
                    Push();
                }
                else
                {
                    Move();
                }
            }

            GameEngine.Instance.Debug.SetData("Destination", level.GetIndexAtPosition(_destination).ToString());
            GameEngine.Instance.Debug.SetData("PathObstructed", AIHelper.IsPathObstructed(level.GetIndexAtPosition(agent.Position), level.GetIndexAtPosition(enemy.Position), level).ToString());
        }

        private void Push()
        {
            if (GameEngine.Instance.Random.NextDouble() < GameConstant.AI_PUSH_CHANCE)
            {
                Vector3 direction;
                direction = enemy.Position - agent.Position;
                direction.Normalize();
                agentControllable.Move(new Vector2(direction.Z * 0.01f, direction.X * 0.01f), _gameTime);
                agentControllable.Push(_gameTime);
            }
        }

        private void Move()
        {
            UpdateDirection();
            //Avoid obstacles if commited to avoiding or if getting close to an obstacle
            if (_avoidCount > 0
                || AIHelper.IsPathObstructed(level.GetIndexAtPosition(agent.Position),
                level.GetIndexAtPosition(agent.Position + agentMovable.Velocity * GameConstant.AI_EVADE_TENDENCY)
                ,level))
            {
                if(_avoidCount == 0)
                    _avoidDirection = GetAvoidDirection(_gameTime);

                if (_avoidCount >= GameConstant.AI_AVOID_TIME)
                    _avoidCount = 0;
                else
                    _avoidCount += _gameTime.ElapsedGameTime.Milliseconds;

                agentControllable.Move(new Vector2(_avoidDirection.Y, _avoidDirection.X), _gameTime);
            }
            else
            {
                MoveToDestination();
            }
        }

        private void MoveToDestination()
        {
            Vector3 direction;
            GameEngine.Instance.Debug.SetData("Avoiding", false.ToString());
            direction = new Vector3(_destination.X, 0, _destination.Z) - agent.Position;
            direction.Normalize();
            agentControllable.Move(new Vector2(direction.Z, direction.X), _gameTime);
        }

        private void UpdateDirection()
        {
            //if destination is reached or destination is obstructed or its time to change destination
            if (level.GetIndexAtPosition(agent.Position) == level.GetIndexAtPosition(_destination) ||
                AIHelper.IsPathObstructed(level.GetIndexAtPosition(agent.Position), level.GetIndexAtPosition(_destination), level) ||
                _changeDestinationCount >= GameConstant.AI_DESTINATION_CHANGE_TIME)
            {
                _destination = NewDestination();
                _changeDestinationCount = 0;
            }
            else
            {
                _changeDestinationCount += _gameTime.ElapsedGameTime.Milliseconds;
            }
        }

        /// <summary>
        /// Randomizes a new destination for the AI to go to.
        /// The new destination to go to won't be obstructed.
        /// </summary>
        private Vector3 NewDestination()
        {
            int[,] map = level.LevelDesign;
            List<Vector3> possibleDestinations = new List<Vector3>();
            
            for(int y = 0; y < GameConstant.LVL_LEVELSIZE; y++){
                for (int x = 0; x < GameConstant.LVL_LEVELSIZE; x++)
                {
                    if (AIHelper.IsMovableTile(new Vector2(x,y), level))
                    {
                        if (!AIHelper.IsPathObstructed(level.GetIndexAtPosition(agent.Position), new Vector2(x, y), level))
                        {
                            possibleDestinations.Add(level.GetPositionForTile(x, y));
                        }
                    }
                }
            }
            if (possibleDestinations.Count > 0)
                return possibleDestinations[GameEngine.Instance.Random.Next(possibleDestinations.Count - 1)];
            else
                return agent.Position;
        }

        public override string ToString()
        {
            return "Evade";
        }
    }
}
